home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Snippets / PNL Libraries / MyEditObject.p < prev    next >
Text File  |  1996-06-01  |  17KB  |  633 lines

  1. unit MyEditObject;
  2.  
  3. interface
  4.  
  5.     uses
  6.         Dialogs;
  7.         
  8.     type
  9.         EditObject = object
  10.                 window: DialogPtr;
  11.                 titem: integer;
  12.                 vcontrol, hcontrol: ControlHandle;
  13.                 te: TEHandle;
  14.                 titemr: Rect;
  15.                 hasgrow, drawgrow: boolean; { hasgrow -> leave room for grow icon, drawgrow -> draw it during updates }
  16.                 doubleClickTime, tripleClickTime: longint;
  17.                 readonly: boolean;
  18.                 undotext: Handle;
  19.                 undostart, undoend, undoselstart, undoselend: integer;
  20.                 undoopen: boolean;
  21.                 modified: boolean;
  22.                 procedure Create (dlg: DialogPtr; item, width: integer; vscroll, hscroll, hasgrowb, drawgrowb, static: boolean);
  23.                 procedure Destroy;
  24.                 procedure Adjust;
  25.                 procedure Resize;
  26.                 procedure Draw;
  27.                 function EditMenuEnabled: boolean;
  28.                 procedure SetEditMenuItem (item: integer);
  29.                 procedure DoEditMenu (item: integer);
  30.                 procedure DoItemWhere (er: EventRecord; item: integer);
  31.                 procedure DoIdle;
  32.                 procedure DoKey (modifiers: integer; ch: char);
  33.                 procedure DoActivateDeactivate (activate: boolean);
  34.                 procedure ClickLoop;
  35.                 procedure Click (pt: Point; extend: boolean);
  36.                 function WordBreak (text: Ptr; pos: integer; forward: boolean): boolean;
  37.             end;
  38.  
  39. implementation
  40.  
  41.     uses
  42.         TextEdit, Scrap, MyMenus, MyOOMainLoop, BaseGlobals, MyTypes, MyUtils, MySystemGlobals;
  43.  
  44.     var
  45.         teo: EditObject;
  46.         teOriginalClickLoop: ProcPtr;
  47.  
  48. { DON'T EVEN THINK ABOUT LOOKING AT THIS CODE!!!!! }
  49.  
  50.     procedure CallCL (addr: ProcPtr);
  51.     inline
  52.         $205F, $4E90;
  53.  
  54.     procedure SetD0to1;
  55.     inline
  56.         $7001;
  57.  
  58.     function GetD2: longint;
  59.     inline
  60.         $2F42, $0000;
  61.  
  62.     procedure Unlink;
  63.     inline
  64.         $4E5E;
  65.  
  66.     procedure Link;
  67.     inline
  68.         $4E56, $0000;
  69.  
  70. {$PUSH}
  71. {$D-}
  72.   { Turn debug off, lest our qute little SetD0to1 hack gets crunged by TP }
  73.     procedure CallClickLoop;  { There must be a better way to sort out this crap! }
  74.     begin
  75.         Unlink;  { This is a rediculous hack! }
  76.         CallCL(teOriginalClickLoop);
  77.         Link;
  78.         teo.ClickLoop;
  79.         SetD0to1;
  80.     end;
  81.  
  82.     function CallWordBreak (text: Ptr; pos: integer): boolean;
  83.         var
  84.             d2: longint;
  85.     begin
  86.         d2 := GetD2;
  87.         CallWordBreak := teo.WordBreak(text, pos, BAND(d2, $00020000) = 0);
  88.     end;
  89. {$POP}
  90.  
  91.     function FindEOL (te: TEHandle; loc: integer): integer;
  92.     begin
  93.         while (loc < te^^.teLength) and (Ptr(longint(te^^.hText^) + loc)^ <> 13) do
  94.             loc := loc + 1;
  95.         FindEOL := loc;
  96.     end;
  97.  
  98.     procedure EditObject.Click (pt: Point; extend: boolean);
  99.         var
  100.             tc, dct: longint;
  101.             doubleclick, tripleclick: boolean;
  102.             teOriginalWordBreak: ProcPtr;
  103.             eol: integer;
  104.     begin
  105.         SetPort(window);
  106.         tc := TickCount;
  107.         doubleclick := tc < doubleClickTime;
  108.         tripleclick := tc < tripleClickTime;
  109.         teo := self;
  110.         teOriginalClickLoop := te^^.clickLoop;
  111.         te^^.clickLoop := @CallClickLoop;
  112.         teOriginalWordBreak := te^^.wordBreak;
  113.         if tripleclick then
  114.             SetWordBreak(@CallWordBreak, te);
  115.         if extend and tripleclick then begin{ we must fake text edit into not shrinking the selection somehow }
  116.             eol := FindEOL(te, te^^.selStart);  { if start<=clickloc<=EOL(start)<selEnd }
  117.             if (te^^.selStart <= te^^.clickloc) and (te^^.clickloc <= eol) and (eol < te^^.selEnd) then
  118.                 TESetSelect(te^^.clickloc, te^^.selEnd, te);
  119.         end;
  120.         TEClick(pt, extend, te);
  121.         tc := TickCount;
  122.         dct := GetDblTime;
  123.         doubleClickTime := tc + dct;
  124.         if doubleclick then
  125.             tripleClickTime := tc + dct;
  126.         te^^.clickLoop := teOriginalClickLoop;
  127.         te^^.wordBreak := teOriginalWordBreak;
  128.  
  129.         if readonly and (te^^.selStart = te^^.selEnd) then begin  { kludge to make the carret go away }
  130.             TEDeactivate(te);
  131.             TEActivate(te);
  132.         end;
  133.  
  134.     end;
  135.  
  136.     function DirtyKey (ch: char): boolean;
  137.     begin
  138.         case ord(ch) of
  139.             homeChar, endChar, helpChar, pageUpChar, pageDownChar, leftArrowChar, rightArrowChar, upArrowChar, downArrowChar: 
  140.                 DirtyKey := false;
  141.             otherwise
  142.                 DirtyKey := true;
  143.         end;
  144.     end;
  145.  
  146.     procedure EditObject.Create (dlg: DialogPtr; item, width: integer; vscroll, hscroll, hasgrowb, drawgrowb, static: boolean);
  147.         var
  148.             dr, vr: Rect;
  149.             k:integer;
  150.             h:Handle;
  151.     begin
  152.         readonly := static;
  153.         doubleClickTime := -1;
  154.         tripleClickTime := -1;
  155.         SetPort(dlg);
  156.         window := dlg;
  157.         titem := item;
  158.         hasgrow := hasgrowb;
  159.         drawgrow := drawgrowb;
  160.         if vscroll then begin
  161.             SetRect(dr, 0, 0, 16, 100);
  162.             vcontrol := NewControl(window, dr, '', true, 0, 0, 0, scrollBarProc, 0);
  163.         end
  164.         else
  165.             vcontrol := nil;
  166.         if hscroll then begin
  167.             SetRect(dr, 0, 0, 100, 16);
  168.             hcontrol := NewControl(window, dr, '', true, 0, 0, 0, scrollBarProc, 0);
  169.         end
  170.         else
  171.             hcontrol := nil;
  172.         GetDialogItem(dlg, titem, k, h, dr);
  173.         titemr := dr;
  174.         EraseRect(dr);
  175.         vr := dr;
  176.         dr.right := dr.left + width;
  177.         te := TENew(dr, vr);
  178.         TEAutoView(true, te);
  179.         undotext := NewHandle(0);
  180.         undostart := -1;
  181.         undoopen := false;
  182.         modified := false;
  183.         Resize;
  184.     end;
  185.  
  186.     procedure EditObject.Destroy;
  187.     begin
  188.         TEDispose(te);
  189.         DisposeHandle(undotext);
  190.         dispose(self);
  191.     end;
  192.  
  193.     procedure AdjustTE (te: TEHandle; hc, vc: integer);
  194. {Scroll the TERec around to match up to the potentially updated scrollbar}
  195. {values. This is really useful when the window resizes such that the}
  196. {scrollbars become inactive and the TERec had been previously scrolled.}
  197.     begin
  198.         with te^^ do
  199.             TEScroll((viewRect.left - destRect.left) - hc, (viewRect.top - destRect.top) - (vc * lineHeight), te);
  200.     end; {AdjustTE}
  201.  
  202.     function AdjustHV (isVert: BOOLEAN; control: ControlHandle; te: TEHandle; canRedraw: BOOLEAN): integer;
  203. {Calculate the new control maximum value and current value, whether it is the horizontal or}
  204. {vertical scrollbar. The vertical max is calculated by comparing the number of lines to the}
  205. {vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document}
  206. {width to the width of the viewRect. The current values are set by comparing the offset between}
  207. {the view and destination rects. If necessary and we canRedraw, have the control be re-drawn by}
  208. {calling ShowControl.}
  209.         var
  210.             value, lines, max: INTEGER;
  211.             oldValue, oldMax: INTEGER;
  212.             cliprgn: RgnHandle;
  213.             r: Rect;
  214.     begin
  215.         oldValue := GetControlValue(control);
  216.         oldMax := GetControlMaximum(control);
  217.         with te^^ do begin
  218.             if isVert then begin
  219.                 lines := nLines;
  220.         {since nLines isn’t right if the last character is a return, check for that case}
  221.                 if (teLength > 0) & (Ptr(ORD(hText^) + teLength - 1)^ = 13) then
  222.                     lines := lines + 1;
  223.                 max := lines - ((viewRect.bottom - viewRect.top) div lineHeight);
  224.             end
  225.             else
  226.                 max := destRect.right - destRect.left - (viewRect.right - viewRect.left);
  227.             if max < 0 then
  228.                 max := 0;            {check for negative values}
  229.             if isVert then
  230.                 value := (viewRect.top - destRect.top) div lineHeight
  231.             else
  232.                 value := viewRect.left - destRect.left;
  233.             if value < 0 then
  234.                 value := 0
  235.             else if value > max then
  236.                 value := max;                    {pin the value to within range}
  237.         end;
  238.         SetPort(te^^.inPort);
  239.         cliprgn := NewRgn;
  240.         GetClip(clipRgn);
  241.         SetRect(r, 0, 0, 0, 0);
  242.         ClipRect(r);
  243.         SetControlMaximum(control, max);
  244.         SetClip(clipRgn);
  245.         DisposeRgn(clipRgn);
  246.         SetControlValue(control, value);
  247.         if canRedraw and ((max <> oldMax) or (value <> oldValue)) then
  248.             ShowControl(control);            {check to see if the control can be re-drawn}
  249.         AdjustHV := value;
  250.     end; {AdjustHV}
  251.  
  252.     procedure EditObject.Adjust;
  253.         var
  254.             hc, vc: integer;
  255.     begin
  256.         vc := AdjustHV(true, vcontrol, te, false);
  257.         hc := AdjustHV(false, hcontrol, te, false);
  258.         AdjustTE(te, hc, vc);
  259.     end; {AdjustScrollValues}
  260.  
  261.     procedure EditObject.Resize;
  262.         const
  263.             invis = 0;
  264.             vis = 255;
  265.             inset = 3;
  266.         var
  267.             vr, tr: Rect;
  268.             pt: Point;
  269.             k: integer;
  270.             h: Handle;
  271.             ht: integer;
  272.             hc, vc: integer;
  273.     begin
  274.         SetPort(window);
  275.         EraseRect(titemr);
  276.         GetDialogItem(window, titem, k, h, tr);
  277.         titemr := tr;
  278.         InvalRect(tr);
  279.         vr := tr;
  280.         InsetRect(vr, inset, inset);
  281.         if hcontrol <> nil then
  282.             vr.bottom := vr.bottom - 15;
  283.         if vcontrol <> nil then
  284.             vr.right := vr.right - 15;
  285.         vr.bottom := vr.top + (vr.bottom - vr.top) div te^^.lineHeight * te^^.lineHeight;
  286.  
  287.         pt := vr.topLeft;
  288.         SubPt(te^^.viewRect.topLeft, pt);
  289.         OffsetRect(te^^.destRect, pt.h, pt.v);
  290.  
  291.         te^^.viewRect := vr;
  292.  
  293.         if vcontrol <> nil then begin
  294.             vcontrol^^.contrlVis := invis;
  295.             MoveControl(vcontrol, tr.right - 16, tr.top);
  296.             ht := tr.bottom - tr.top;
  297.             if hasgrow then
  298.                 ht := ht - 15;
  299.             SizeControl(vcontrol, 16, ht);
  300.             vc := AdjustHV(true, vcontrol, te, false);
  301.             vcontrol^^.contrlVis := vis;
  302.         end;
  303.         if hcontrol <> nil then begin
  304.             hcontrol^^.contrlVis := invis;
  305.             MoveControl(hcontrol, tr.left, tr.bottom - 16);
  306.             ht := tr.right - tr.left;
  307.             if hasgrow or (vcontrol <> nil) then
  308.                 ht := ht - 15;
  309.             SizeControl(hcontrol, ht, 16);
  310.             hc := AdjustHV(false, hcontrol, te, false);
  311.             hcontrol^^.contrlVis := vis;
  312.         end;
  313.         AdjustTE(te, hc, vc);
  314.     end;
  315.  
  316.     procedure EditObject.Draw;
  317.         var
  318.             r: Rect;
  319.             k: integer;
  320.             h: Handle;
  321.     begin
  322.         GetDialogItem(window, titem, k, h, r);
  323.         EraseRect(r);
  324.         if drawgrow then begin
  325.             DrawGrowIcon(window);
  326.         end;
  327.         if vcontrol <> nil then begin
  328.             Draw1Control(vcontrol);
  329.         end;
  330.         if hcontrol <> nil then begin
  331.             Draw1Control(hcontrol);
  332.         end;
  333.         EraseRect(te^^.viewRect);
  334.         TEUpdate(te^^.viewRect, te);
  335.     end;
  336.  
  337.     procedure EditObject.DoActivateDeactivate (activate: boolean);
  338.     begin
  339.         if drawgrow then
  340.             DrawGrowIcon(window);
  341.         if activate then
  342.             TEActivate(te)
  343.         else
  344.             TEDeactivate(te);
  345.     end;
  346.  
  347. { Common algorithm for pinning the value of a control. It returns the actual amount }
  348. { the value of the control changed. }
  349.     procedure CommonAction (control: ControlHandle; var amount: integer);
  350.         var
  351.             value, max: integer;
  352.     begin
  353.         value := GetControlValue(control);
  354.         max := GetControlMaximum(control);
  355.         amount := value - amount;
  356.         if (amount <= 0) then
  357.             amount := 0
  358.         else if (amount >= max) then
  359.             amount := max;
  360.         SetControlValue(control, amount);
  361.         amount := value - amount;   { calculate true change }
  362.     end; { CommonAction  }
  363.  
  364.     var
  365.         actionTE: TEHandle;
  366.  
  367. { Determines how much to change the value of the vertical scrollbar by and how }
  368. { much to scroll the TE record.}
  369.     procedure VActionProc (control: ControlHandle; part: integer);
  370.         var
  371.             amount: integer;
  372.             window: WindowPtr;
  373.     begin
  374.         if (part <> 0) then begin
  375.             window := control^^.contrlOwner;
  376.             case part of
  377.                 kInUpButtonControlPart, kInDownButtonControlPart:        { one line  }
  378.                     amount := 1;
  379.                 kInPageUpControlPart, kInPageDownControlPart:            { one page  }
  380.                     with actionTE^^, viewRect do
  381.                         amount := (bottom - top) div lineHeight;
  382.             end;
  383.             if ((part = kInDownButtonControlPart) or (part = kInPageDownControlPart)) then
  384.                 amount := -amount;        { reverse direction for a downer  }
  385.             CommonAction(control, amount);
  386.             if (amount <> 0) then
  387.                 TEScroll(0, amount * actionTE^^.lineHeight, actionTE);
  388.         end;
  389.     end; { VActionProc }
  390.  
  391. { Determines how much to change the value of the horizontal scrollbar by and how }
  392. { much to scroll the TE record. }
  393.     procedure HActionProc (control: ControlHandle; part: integer);
  394.         var
  395.             amount: integer;
  396.             window: WindowPtr;
  397.     begin
  398.         if (part <> 0) then begin
  399.             window := control^^.contrlOwner;
  400.             case part of
  401.                 kInUpButtonControlPart, kInDownButtonControlPart:        { a few pixels }
  402.                     amount := 8;
  403.                 kInPageUpControlPart, kInPageDownControlPart:            { a page width }
  404.                     with actionTE^^.viewRect do
  405.                         amount := (right - left);
  406.             end;
  407.             if ((part = kInDownButtonControlPart) or (part = kInPageDownControlPart)) then
  408.                 amount := -amount;        { reverse direction }
  409.             CommonAction(control, amount);
  410.             if (amount <> 0) then
  411.                 TEScroll(amount, 0, actionTE);
  412.         end;
  413.     end; { HActionProc }
  414.  
  415. { Gets called from CallClickLoop which in turn }
  416. { is called by the TEClick toolbox routine. Saves the window's clip region, }
  417. { sets it to the portRect, adjusts the scrollbar values to match the TE scroll }
  418. { amount, then restores the clip region. }
  419.     procedure EditObject.ClickLoop;
  420.         var
  421.             region: RgnHandle;
  422.             vc, hc: integer;
  423.     begin
  424.         SetPort(window);
  425.         region := NewRgn;
  426.         GetClip(region);                { save the old clip }
  427.         ClipRect(window^.portRect);        { set the new clip }
  428.         vc := AdjustHV(true, vcontrol, te, false);
  429.         hc := AdjustHV(false, hcontrol, te, false);
  430.         SetClip(region);                { restore the old clip }
  431.         DisposeRgn(region);
  432.     end; { PascalClikLoop }
  433.  
  434.     function EditObject.WordBreak (text: Ptr; pos: integer; forward: boolean): boolean;
  435.     begin
  436.         if forward then
  437.             WordBreak := (pos > 0) and (Ptr(longint(text) + pos - 1)^ = 13)
  438.         else
  439.             WordBreak := Ptr(longint(text) + pos)^ = 13
  440.     end;
  441.  
  442.     procedure EditObject.DoItemWhere (er: EventRecord; item: integer);
  443.         var
  444.             control: ControlHandle;
  445.             value, part: integer;
  446.             uss, use: integer;
  447.     begin
  448.         uss := te^^.selStart;
  449.         use := te^^.selEnd;
  450.         SetPort(window);
  451.         GlobalToLocal(er.where);
  452.         part := FindControl(er.where, window, control);
  453.         if part = 0 then begin
  454.             if PtInRect(er.where, te^^.viewRect) then
  455.                 Click(er.where, BAND(er.modifiers, shiftKey) <> 0)
  456.         end
  457.         else begin
  458.             if part = kInIndicatorControlPart then begin
  459.                 value := GetControlValue(control);
  460.                 part := TrackControl(control, er.where, nil);
  461.                 if part <> 0 then begin
  462.                     value := value - GetControlValue(control);
  463.                     if value <> 0 then
  464.                         if control = vcontrol then
  465.                             TEScroll(0, value * te^^.lineHeight, te)
  466.                         else
  467.                             TEScroll(value, 0, te);
  468.                 end;
  469.             end
  470.             else begin
  471.                 actionTE := te;
  472.                 if control = vcontrol then
  473.                     value := TrackControl(control, er.where, @VActionProc)
  474.                 else
  475.                     value := TrackControl(control, er.where, @HActionProc);
  476.             end;
  477.         end;
  478.         if (uss <> te^^.selStart) or (use <> te^^.selEnd) then
  479.             undoopen := false;
  480.     end;
  481.  
  482.     function EditObject.EditMenuEnabled: boolean;
  483.         var
  484.             i: integer;
  485.             offset: longint;
  486.     begin
  487.         for i := EMundo to EMselectall do
  488.             if i <> EMundo + 1 then
  489.                 SetEditMenuItem(i);
  490.         EditMenuEnabled := false;
  491.         if (te^^.selStart < te^^.selEnd) or (te^^.teLength > 0) then { Select All, Copy }
  492.             EditMenuEnabled := true;
  493.         if not readonly and ((undostart >= 0) or (GetScrap(nil, 'TEXT', offset) > 0)) then { Undo, Paste }
  494.             EditMenuEnabled := true;
  495.     end;
  496.  
  497.     procedure EditObject.SetEditMenuItem (item: integer);
  498.         var
  499.             offset: longint;
  500.     begin
  501.         case item of
  502.             EMundo: 
  503.                 SetIDItemEnable(M_Edit, item, undostart >= 0);
  504.             EMcut, EMclear: 
  505.                 SetIDItemEnable(M_Edit, item, not readonly and (te^^.selStart < te^^.selEnd));  { Can cut,clear iff there is a selection and its not readonly}
  506.             EMcopy: 
  507.                 SetIDItemEnable(M_Edit, item, te^^.selStart < te^^.selEnd);  { Can copy iff there is a selection }
  508.             EMpaste: 
  509.                 SetIDItemEnable(M_Edit, item, not readonly and (GetScrap(nil, 'TEXT', offset) > 0));        {Paste is enabled for app. windows}
  510.             EMselectall: 
  511.                 SetIDItemEnable(M_Edit, item, te^^.teLength > 0);  { Can select all iff there is something to select }
  512.             otherwise
  513.         end;
  514.     end;
  515.  
  516.     procedure CopyUndoSelection (h: Handle; te: TEHandle);
  517.     begin
  518.         SetHandleSize(h, te^^.selEnd - te^^.selStart);
  519.         BlockMove(Ptr(longint(te^^.hText^) + te^^.selStart), h^, te^^.selEnd - te^^.selStart);
  520.     end;
  521.  
  522.     procedure EditObject.DoEditMenu (item: integer);
  523.         var
  524.             oe: OSErr;
  525.             loe: longint;
  526.             th: Handle;
  527.             uss, use: integer;
  528.     begin
  529.         undoopen := false;
  530.         case item of
  531.             EMcopy:  begin
  532.                 TECopy(te);
  533.                 loe := ZeroScrap;
  534.                 oe := TEToScrap;
  535.             end;
  536.             EMselectall:  begin
  537.                 SetPort(window);
  538.                 TESetSelect(0, maxLongInt, te);
  539.             end;
  540.             EMcut:  begin
  541.                 CopyUndoSelection(undotext, te);
  542.                 undoselstart := te^^.selStart;
  543.                 undoselend := te^^.selEnd;
  544.                 undostart := te^^.selStart;
  545.                 undoend := undostart;
  546.                 TECut(te);
  547.                 loe := ZeroScrap;
  548.                 oe := TEToScrap;
  549.                 modified := true;
  550.             end;
  551.             EMclear:  begin
  552.                 CopyUndoSelection(undotext, te);
  553.                 undoselstart := te^^.selStart;
  554.                 undoselend := te^^.selEnd;
  555.                 undostart := te^^.selStart;
  556.                 undoend := undostart;
  557.                 TEDelete(te);
  558.                 modified := true;
  559.             end;
  560.             EMpaste:  begin
  561.                 oe := TEFromScrap;
  562.                 if TEGetScrapLength + (te^^.teLength - (te^^.selEnd - te^^.selStart)) > 32000 then
  563.                     AlertUser(paste_to_big)
  564.                 else begin
  565.                     CopyUndoSelection(undotext, te);
  566.                     undoselstart := te^^.selStart;
  567.                     undoselend := te^^.selEnd;
  568.                     undostart := te^^.selStart;
  569.                     TEPaste(te);
  570.                     undoend := te^^.selEnd;
  571.                     modified := true;
  572.                 end;
  573.             end;
  574.             EMundo:  begin
  575.                 uss := undoselstart;
  576.                 use := undoselend;
  577.                 undoselstart := te^^.selStart;
  578.                 undoselend := te^^.selEnd;
  579.                 th := NewHandle(undoend - undostart);
  580.                 BlockMove(Ptr(longint(te^^.hText^) + undostart), th^, undoend - undostart); { save undo for redo }
  581.                 TESetSelect(undostart, undoend, te);
  582.                 TEDelete(te);
  583.                 HLock(undotext);
  584.                 TEInsert(undotext^, GetHandleSize(undotext), te);
  585.                 DisposeHandle(undotext);
  586.                 undotext := th;
  587.                 undoend := te^^.selEnd;
  588.                 TESetSelect(uss, use, te);
  589.             end;
  590.             otherwise
  591.         end;
  592.     end;
  593.  
  594.     procedure EditObject.DoIdle;
  595.     begin
  596.         if not readonly then
  597.             TEIdle(te);
  598.     end;
  599.  
  600.     procedure EditObject.DoKey (modifiers: integer; ch: char);
  601.         procedure Doit;
  602.         begin
  603.             if BAND(modifiers, cmdKey) = 0 then
  604.                 TEKey(ch, te);
  605.             Adjust;
  606.         end;
  607.         var
  608.             dk: boolean;
  609.     begin
  610.         dk := DirtyKey(ch);
  611.         if dk then begin
  612.             if not readonly then begin
  613.                 modified := true;
  614.                 if not undoopen then begin
  615.                     CopyUndoSelection(undotext, te);
  616.                     undoselstart := te^^.selStart;
  617.                     undoselend := te^^.selEnd;
  618.                     undostart := te^^.selStart;
  619.                 end;
  620.                 Doit;
  621.                 undoend := te^^.selEnd;
  622.             end
  623.             else begin
  624.                 SysBeep(1);
  625.             end;
  626.         end
  627.         else begin
  628.             Doit;
  629.         end;
  630.         undoopen := dk;
  631.     end;
  632.  
  633. end.